package org.hackystat.devcathlon.engine.event.impl;

import static org.junit.Assert.assertEquals;
import org.hackystat.devcathlon.engine.event.Event;
import org.hackystat.devcathlon.engine.event.EventScore;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.team.Team;
import org.hackystat.devcathlon.engine.test.DevcathlonTestHelper;
import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.dailyprojectdata.client.DailyProjectDataClient;
import org.hackystat.dailyprojectdata.resource.devtime.jaxb.DevTimeDailyProjectData;
import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.engine.user.User;
import org.hackystat.sensorbase.resource.projects.jaxb.Project;
import org.hackystat.utilities.tstamp.Tstamp;
import org.junit.Test;
import org.junit.BeforeClass;


/**
 * Tests the CollectiveOwnership event.
 *
 * @author Anthony Du, John Ancheta
 */
public class TestCollectiveOwnership extends DevcathlonTestHelper {
  private String teamName = "TestTeam";
  private String teamOwner = "TeamOwner";
  private String matchName = "TestMatch";
  private String member1 = "Member1";
  private String member2 = "Member2";
  
  /** Wakeup interval in minutes. */
  private static int wakeupInterval = 1440;
  
  /** Minutes needed for points. */
  private static int devTimeMinutes = 60;
  
  /** Points awarded. */
  private static int awardPoints = 10;

  /** Percentage of files that should be worked on. */
  private static int filesPercentage = 50;
  
  /** Number of developers that should be working on the files. */
  private static int minNumDevelopers = 2;
  
  /** Minutes needed for points. */
  private static int bonusDevTimeMinutes = 60;
  
  /** Points awarded. */
  private static int bonusAwardPoints = 5;
  
  /** Percentage of files that should be worked on. */
  private static int bonusFilesPercentage = 90;

  /** Number of developers that should be working on the files. */ 
  private static int bonusMinNumDevelopers = 2;

  
  /**
   * Sets up the DevcathlonConfiguration for use in these test cases. 
   * @throws Exception If problems occur setting up the configuration. 
   */
  @BeforeClass
  public static void setupConfiguration() throws Exception {
    // Set up the testing DevcathlonConfiguration for the event CollectiveOwnership.
    String eventName = CollectiveOwnership.eventName;
    ConfigurationManager configManager = ConfigurationManager.getInstance();
    configManager.setBaseScoreWeight(eventName, 1);
    configManager.getConfiguration(eventName);
    configManager.setWakeupInterval(eventName, wakeupInterval);
    
    configManager.addProperty(eventName, CollectiveOwnership.devTimeMinutesKey, 
                              Integer.toString(devTimeMinutes));
    configManager.addProperty(eventName, CollectiveOwnership.awardPointsKey, 
                              Integer.toString(awardPoints));
    configManager.addProperty(eventName, CollectiveOwnership.filesPercentageKey, 
                              Integer.toString(filesPercentage));
    configManager.addProperty(eventName, CollectiveOwnership.minNumDevelopersKey, 
                              Integer.toString(minNumDevelopers));
    configManager.addProperty(eventName, CollectiveOwnership.bonusDevTimeMinutesKey, 
                              Integer.toString(bonusDevTimeMinutes));
    configManager.addProperty(eventName, CollectiveOwnership.bonusAwardPointsKey, 
                              Integer.toString(bonusAwardPoints));
    configManager.addProperty(eventName, CollectiveOwnership.bonusFilesPercentageKey, 
                              Integer.toString(bonusFilesPercentage));
    configManager.addProperty(eventName, CollectiveOwnership.bonusMinNumDevelopersKey, 
                              Integer.toString(bonusMinNumDevelopers));
  }
 
  /**
   * Tests configuation.
   *
   * @throws Exception If there is a bug.
   */
  @Test
  public void testConfiguration() throws Exception {
    CollectiveOwnership collectiveOwnership = new CollectiveOwnership();
    assertEquals("Check wakeupInterval", wakeupInterval, 
                 collectiveOwnership.getWakeupInterval());
    assertEquals("Check devTimeMinutes", devTimeMinutes, 
                 collectiveOwnership.getDevTimeMinutes());
    assertEquals("Check awardPoints", awardPoints, 
                 collectiveOwnership.getAwardPoints());
    assertEquals("Check filesPercentage", filesPercentage, 
                 collectiveOwnership.getFilesPercentage());
    assertEquals("Check minNumDevelopers", minNumDevelopers, 
                 collectiveOwnership.getMinNumDevelopers());
    assertEquals("Check bonusDevTimeMinutes", bonusDevTimeMinutes, 
                 collectiveOwnership.getBonusDevTimeMinutes());
    assertEquals("Check bonusAwardPoints", bonusAwardPoints, 
                 collectiveOwnership.getBonusAwardPoints());
    assertEquals("Check bonusFilesPercentage", bonusFilesPercentage, 
                 collectiveOwnership.getBonusFilesPercentage());
    assertEquals("Check bonusMinNumDevelopers", bonusMinNumDevelopers, 
                 collectiveOwnership.getBonusMinNumDevelopers());
  }
  
  /**
   * Tests score has bonus award points.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testHasBonusAwardPoints() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new CollectiveOwnership();
    
    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);
    Project project = getProject(teamOwner, teamName);
    
    XMLGregorianCalendar devEventEndTime = Tstamp.makeTimestamp();
    XMLGregorianCalendar devEventStartTime = Tstamp.incrementMinutes(devEventEndTime,
                                                                     -1 * wakeupInterval);
    for (User user : team.getMembers()) {
      for (int i = 1; i <= 4; i++) {
        makeDevTimeData(user, project, devEventStartTime, devTimeMinutes, "/file" + i + ".java");
        devEventStartTime = Tstamp.incrementMinutes(devEventStartTime, devTimeMinutes + 1);
      }
    }
    
    // Test the event with no sensor data.
    EventScore score = event.computeScore(match, team, devEventEndTime);
    
    assertEquals("Check base 1a", awardPoints, score.getBaseScore());
    assertEquals("Check bonus 1a", bonusAwardPoints,
                 score.getBonusScore(CollectiveOwnership.bonusName));
    assertEquals("Check points 1a", awardPoints + bonusAwardPoints,
    event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests score has no bonus award points, but has award points.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testHasNoBonusAwardPoints() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new CollectiveOwnership();
    
    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);
    Project project = getProject(teamOwner, teamName);
    
    XMLGregorianCalendar devEventEndTime = Tstamp.makeTimestamp();
    XMLGregorianCalendar devEventStartTime = Tstamp.incrementMinutes(devEventEndTime,
                                                                     -1 * wakeupInterval);
    
    int i = 1;
    for (User user : team.getMembers()) {
      for (; i <= 4; i++) {
        makeDevTimeData(user, project, devEventStartTime, devTimeMinutes, "/file" + i + ".java");
        devEventStartTime = Tstamp.incrementMinutes(devEventStartTime, devTimeMinutes + 1);
      }
      makeBuildData(user, project, Tstamp.incrementMinutes(devEventStartTime, 1), true);
      i = 2;
    }

    // Test the event with no sensor data.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 1", awardPoints, score.getBaseScore());
    assertEquals("Check bonus 1", 0, score.getBonusScore(CollectiveOwnership.bonusName));
    assertEquals("Check points 1", awardPoints, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests score has no bonus and award points.
   * 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testHasNoBonusAndAwardPoints() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new CollectiveOwnership();
    
    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);
    Project project = getProject(teamOwner, teamName);
    
    XMLGregorianCalendar devEventEndTime = Tstamp.makeTimestamp();
    XMLGregorianCalendar devEventStartTime = Tstamp.incrementMinutes(devEventEndTime,
                                                                     -1 * wakeupInterval);
    
    int i = 1;
    for (User user : team.getMembers()) {
      for (; i <= 3; i++) {
        makeDevTimeData(user, project, devEventStartTime, devTimeMinutes, "/file" + i + ".java");
        devEventStartTime = Tstamp.incrementMinutes(devEventStartTime, devTimeMinutes + 1);
      }
      makeBuildData(user, project, Tstamp.incrementMinutes(devEventStartTime, 1), true);
      i = 3;
    }
    
    // Test the event with no sensor data.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 1", 0, score.getBaseScore());
    assertEquals("Check bonus 1", 0, score.getBonusScore(CollectiveOwnership.bonusName));
    assertEquals("Check points 1", 0, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  /**
   * Tests this event when there is no sensor data for this team.
   * @throws Exception If there is a bug.
   */
  @Test
  public void testNoData() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new CollectiveOwnership();
    
    // Create a simple match containing a single team, a single project, and this event.
    Match match = addSimpleMatch(matchName, team, getProject(teamOwner, teamName), event);
    
    // Test the event with no sensor data.
    EventScore score = event.computeScore(match, team, Tstamp.makeTimestamp());
    assertEquals("Check base 1", 0, score.getBaseScore());
    assertEquals("Check bonus 1", 0, 
                 score.getBonusScore(CollectiveOwnership.bonusName));
    assertEquals("Check points 1", 0, event.computePoints(score));
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
  
  /**
   * Tests the makeDevTimeData() helper method, since there isn't yet any Event using it. 
   * @throws Exception If there is a bug.
   */
  @Test
  public void testDevTime() throws Exception {
    // Create the team. 
    Team team = makeTeam(teamName, teamOwner, member1, member2);
    
    // Now create the event instance.
    Event event = new CollectiveOwnership();
    
    // Create a simple match containing a single team, a single project, and this event.
    Project project = getProject(teamOwner, teamName);
    Match match = addSimpleMatch(matchName, team, project, event);
    
    // Call makeDevTimeData to make 30 minutes of DevTime (this generates 6 DevEvents). 
    XMLGregorianCalendar startTime = Tstamp.makeTimestamp();
    makeDevTimeData(team.getOwner(), project, startTime, 30);
    DailyProjectDataClient dpdClient = match.getDailyProjectDataClient(team);
    
    // Now get the DPD data and check to make sure that 30 minutes of DevTime is computed. 
    DevTimeDailyProjectData dpdData = dpdClient.getDevTime(team.getOwner().getEmail(), 
        project.getName(), startTime);
    assertEquals("Testing makeDevTime", 30, dpdData.getTotalDevTime().intValue());
    
    // Cleanup.
    deleteTeam(teamName);
    deleteMatch(matchName);
  }
}
